package com.coc.action;

import java.io.ByteArrayOutputStream;
import java.io.DataInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.PrintWriter;
import java.security.MessageDigest;
import java.util.Enumeration;
import java.util.zip.InflaterInputStream;

import javax.servlet.ServletException;
import javax.servlet.ServletInputStream;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.apache.log4j.Logger;
import org.springframework.context.ApplicationContext;
import org.springframework.web.context.support.WebApplicationContextUtils;

import net.sf.json.JSONArray;
import net.sf.json.JSONObject;

import com.coc.bean.CcEloUser;
import com.coc.bean.CcMap;
import com.coc.dao.BuildSettingXML;
import com.coc.dao.EloUserDAO;
import com.coc.dao.MemCachedDAO;
import com.coc.service.CocBattleService;
import com.coc.service.CocMapService;
import com.coc.service.CocUserService;
import com.coc.util.MemCacheUtil;
import com.coc.util.MemException;
import com.coc.util.SignException;
import com.schooner.MemCached.MemcachedItem;

public class Act extends HttpServlet {
	static Logger logger = Logger.getLogger(DataAction.class.getName());

	public void doPost(HttpServletRequest request, HttpServletResponse response)
			throws ServletException, IOException {
		
		PrintWriter out = response.getWriter();
		response.setContentType("text/html;charset=UTF-8");
		response.setCharacterEncoding("UTF-8");
		String contentType = request.getContentType();
		
		//application/octet-stream
		if (!isLoginV(request)) {
			out.print("{\"error\":{\"type\":\"v\",\"value\":1}}");
			return;
		}
		if (!isLogin(request)) {
			out.print("{\"error\":{\"value\":1}}");
			return;
		}
		int id = Integer.parseInt(request.getParameter("tk").substring(0,
				request.getParameter("tk").indexOf("_")));
		Long curlastact = 0L; // 本次打包方法中最后动作时间
		long casUnique = 0L;// --db中记录
		long lastact = 0L; // --db中记录
		// map_act_XXXXXX
		try {
			MemcachedItem mi = MemCacheUtil.getsObject("map_act_" + id);
			JSONArray ja = null;
			if(contentType!=null&&contentType.equals("application/octet-stream")){
				ja = this.getMethodArrayFromStream(request);
			}else{
				ja = this.getMethodArray(request);
			}
			curlastact = this.getLastT(ja);
			if (mi != null) {
				casUnique = mi.getCasUnique();
				lastact = Long.valueOf(mi.getValue().toString());
			}
			// 负数说明处理中,等1秒继续递归调用
			if (lastact < 0) {
				logger.warn("UID:" + id + " map_acts sleep 1s...");
				Thread.sleep(1000L);
				doPost(request, response);
			}
			// 实际处理的动作包最后实际大等于当前包,
			// 当前包全是过期请求,抛弃并且返回sign1
			if (curlastact <= lastact) {
				logger.warn("expire map_acts  uid:" + id + " ...");
				out.print("{\"sign\":{\"value\":\"" + 1 + "\"}}");
				return;
			}
			// 抢锁,并把curlastact置为负数
			// 如果抢锁失败等1秒继续递归调用(异常捕获处理)
			if (casUnique == 0) {
				MemCacheUtil.setObject("map_act_" + id, -1 * curlastact + "");
	
			} else {
				MemCacheUtil.casObject("map_act_" + id, -1 * curlastact + "",
						casUnique);
			}
			// &map_acts =
			// [{t:1469400255523,b:"LABT|0",a:"move",x:3,y:7},
			// {t:1469400235386,b:"BARK|2",a:"boost_upgrade"},....]
	
			if (ja != null) {
				for (int i = 0; i < ja.size(); i++) {
					JSONObject jo = (JSONObject) ja.get(i);
					String act = jo.getString("a");
					if (act.equals("build")) {
						getCocMapService().MapBuild(request, jo);
					} else if (act.equals("upgrade")) {
						getCocMapService().MapUpgrade(request, jo);
					} else if (act.equals("move")) {
						getCocMapService().MapMove(request, jo);
					} else if (act.equals("sell")) {
						getCocMapService().MapSell(request, jo);
					} else if (act.equals("remove")) {
						getCocMapService().MapCut(request, jo);
					} else if (act.equals("collect")) {
						getCocUserService().Collect(request, jo);
					} else if (act.equals("train")) {
						getCocUserService().Tran(request, jo);
					} else if (act.equals("spell")) {
						getCocUserService().SpellResearch(request, jo);
					} else if (act.equals("lab")) {
						getCocUserService().LabResearch(request, jo);
					} else if (act.equals("lab_get")) {
						getCocUserService().LabGet(request, jo);
					} else if (act.equals("lab_cancel")) {
						getCocUserService().LabCancel(request, jo);
					} else if (act.equals("cancel_upgrade")) {
						getCocMapService().MapCancel(request, jo);
					} else if (act.equals("cancel_train")) {
						getCocUserService().CancelTran(request, jo);
					} else if (act.equals("cancel_spell")) {
						getCocUserService().CancelSpellResearch(request, jo);
					} else if (act.equals("boost_upgrade")) {
						getCocMapService().MapBoost(request, jo);
					} else if (act.equals("boost_train")) {
						getCocUserService().TranBoost(request, jo);
					} else if (act.equals("boost_spell")) {
						getCocUserService().SpellBoost(request, jo);
					} else if (act.equals("boost_lab")) {
						getCocUserService().LabBoost(request, jo);
					} else if (act.equals("boost_mine")) {
						getCocUserService().MineBoost(request, jo);
					}
	
				}
			}
			out.print("{\"sign\":{\"value\":\"" + 1 + "\"}}");
			// 完全成功后吧map_act_id 设置成正数
			MemCacheUtil.setObject("map_act_" + id, curlastact + "");
		} catch (SignException se) {
			// sign错误2:作弊 用login时候缓存的数据回滚
			// 回滚暂时取消
			// if (se.getSign() == 2) {
			// CcMap cm = (CcMap) MemCacheUtil.getObject("cm_bak_" + id);
			// CcEloUser ceu = (CcEloUser) MemCacheUtil.getObject("ceu_bak_" +
			// id);
			// MemCacheUtil.setObject("cm_" + id, cm);
			// getEloUserDAO().saveOrUpdate(ceu);
			// }
			logger.warn("Find sign 2 uid:" + id + " reason:" + se.getMessage());
			// 2） 作弊后的“清理 token”, 清理成 0000
			MemCacheUtil.setObject("map_act_" + id, System.currentTimeMillis()
					+ "");
			MemCacheUtil.setSession(id, "0000");
			out.println("{\"sign\":{\"value\":" + se.getSign()
					+ "},\"reason\":\"" + se.getMessage() + "\"}");
			out.flush();
			out.close();
			// se.printStackTrace();
		} catch (MemException me) {
			// cas错误,没保存成功
			logger.warn("Find cas error uid:" + id + " reason:"
					+ me.getMessage());
			// if (me.getMem_sign() == 0) {
			// CcMap cm = (CcMap) MemCacheUtil.getObject("cm_bak_" + id);
			// CcEloUser ceu = (CcEloUser) MemCacheUtil.getObject("ceu_bak_"
			// + id);
			// MemCacheUtil.setObject("cm_" + id, cm);
			// getEloUserDAO().saveOrUpdate(ceu);
			// }
			//me.printStackTrace();
			try {
				Thread.sleep(1000L);
				doPost(request, response);
			} catch (Exception e) {
				e.printStackTrace();
			}
		} catch (Exception e) {
			MemCacheUtil.setObject("map_act_" + id, lastact + "");
			out.print("{\"error\":{\"value\":0}}");
			out.flush();
			out.close();
			e.printStackTrace();
		}
	
	}

	/**
	 * Constructor of the object.
	 */
	public Act() {
		super();
	}

	/**
	 * Destruction of the servlet. <br>
	 */
	public void destroy() {
		super.destroy(); // Just puts "destroy" string in log
		// Put your code here
	}

	/**
	 * The doGet method of the servlet. <br>
	 * 
	 * This method is called when a form has its tag value method equals to get.
	 * 
	 * @param request
	 *            the request send by the client to the server
	 * @param response
	 *            the response send by the server to the client
	 * @throws ServletException
	 *             if an error occurred
	 * @throws IOException
	 *             if an error occurred
	 */
	public void doGet(HttpServletRequest request, HttpServletResponse response)
			throws ServletException, IOException {

		doPost(request, response);
	}

	/**
	 * The doPost method of the servlet. <br>
	 * 
	 * This method is called when a form has its tag value method equals to
	 * post.
	 * 
	 * @param request
	 *            the request send by the client to the server
	 * @param response
	 *            the response send by the server to the client
	 * @throws ServletException
	 *             if an error occurred
	 * @throws IOException
	 *             if an error occurred
	 */
	public int bytes2int(byte[] bytes) {
		int num = bytes[0] & 0xFF;
		num |= ((bytes[1] << 8) & 0xFF00);
		num |= ((bytes[2] << 16) & 0xFF0000);
		num |= ((bytes[3] << 24) & 0xFF000000);
		return num;
	}

	public long bytes2long(byte[] b) {
		long temp = 0;
		long res = 0;
		for (int i = 0; i < 8; i++) {
			res <<= 8;
			temp = b[i] & 0xff;
			res |= temp;
		}
		return res;
	}

	/**
	 * Initialization of the servlet. <br>
	 * 
	 * @throws ServletException
	 *             if an error occurs
	 */
	public void init() throws ServletException {
		// Put your code here
	}

	public Integer[][] SetMap(Integer[][] map, int x, int y, int size) {
		for (int i = 0; i < size; i++) {
			for (int j = 0; j < size; j++) {
				map[(x - 1 + i)][(y - 1 + j)] = Integer.valueOf(1);
			}
		}
		return map;
	}

	public CocUserService getCocUserService() {
		return (CocUserService) getBean("cocUserService");
	}

	public CocMapService getCocMapService() {
		return (CocMapService) getBean("cocMapService");
	}

	public CocBattleService getCocBattleService() {
		return (CocBattleService) getBean("cocBattleService");
	}

	public BuildSettingXML getBuildSettingXML() {
		return (BuildSettingXML) getBean("buildSettingXML");
	}

	public EloUserDAO getEloUserDAO() {
		return (EloUserDAO) getBean("eloUserDAO");
	}

	public MemCachedDAO getMemCachedDAO() {
		return (MemCachedDAO) getBean("memCachedDAO");
	}

	public Object getBean(String name) {
		ApplicationContext ctx = WebApplicationContextUtils
				.getRequiredWebApplicationContext(this.getServletContext());
		return ctx.getBean(name);
	}

	public boolean isLoginV(HttpServletRequest request) {
		return true;
	}

	public String MD5digest(String message) {
		char[] hexDigits = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9',
				'a', 'b', 'c', 'd', 'e', 'f' };
		try {
			byte[] strTemp = message.getBytes();
			MessageDigest mdTemp = MessageDigest.getInstance("MD5");
			mdTemp.update(strTemp);
			byte[] md = mdTemp.digest();
			int j = md.length;
			char[] str = new char[j * 2];
			int k = 0;
			for (int i = 0; i < j; i++) {
				byte byte0 = md[i];
				str[(k++)] = hexDigits[(byte0 >>> 4 & 0xF)];
				str[(k++)] = hexDigits[(byte0 & 0xF)];
			}
			return new String(str);
		} catch (Exception e) {
		}
		return null;
	}

	public boolean isLogin(HttpServletRequest request) {
		String tk = request.getParameter("tk");
		if (tk == null)
			return false;
		try {
			int uid = Integer.parseInt(tk.substring(0, tk.indexOf("_")));
			String sessionString = MemCacheUtil.getSession(uid);
			String token = tk.substring(tk.indexOf("_") + 1);
			if (sessionString.indexOf(token) != -1) {
				return true;
			}
			return false;
		} catch (Exception e) {
			e.printStackTrace();
		}
		return false;
	}

	public JSONArray getMethodArray(HttpServletRequest request) {
		try {
			
			if (request.getParameter("map_acts") != null) {
				JSONArray ja = JSONArray.fromObject(request
						.getParameter("map_acts"));
				
				return ja;
			}
		} catch (Exception e) {
			e.printStackTrace();
		}
		return null;
	}
	public JSONArray getMethodArrayFromStream(HttpServletRequest request) {
		try {
			InputStream is = request.getInputStream(); 
			
			DataInputStream input = new DataInputStream(new InflaterInputStream(is));
			//System.out.println("str="+input.readUTF());
			
			JSONArray ja = JSONArray.fromObject(input.readUTF());
			return ja;
			
		} catch (Exception e) {
			e.printStackTrace();
		}
		return null;
	} 

	public long getLastT(JSONArray ja) {

		long last = 0L;

		for (int i = 0; i < ja.size(); i++) {
			JSONObject jo = (JSONObject) ja.get(i);
			long lastt = 0L;
			if (jo.get("t") == null) {
				lastt = System.currentTimeMillis();
			} else {
				lastt = Long.valueOf(jo.getString("t"));
			}
			if (lastt > last) {
				last = lastt;
			}
		}

		return last;

	}
}
